﻿using System;
using Simp.CodeModel.CoreFunctions;
using System.Collections.Generic;
using System.Collections;

namespace Simp.CodeModel.CoreFunctions.Sequences
{
	/// <summary>Sequence function taking two parameters:  a first and last.</summary>
	/// <remarks>
	/// The two extrema of the interval are <see cref="First"/> and <see cref="Last"/>.  They do not need to be ordered:
	/// the sequence will have increasing or decreasing value, depending on the order of the extrema.
	/// Interval is defined <c>inclusively</c> with the extrema, ie, the extrama are included in the extrema.
	/// The extrema needs to simplify into an <see cref="IntegerFunction"/> in order for the sequence to be accessed.
	/// </remarks>
	public class IntervalSequenceFunction : ISequenceFunction, IFunctionSerializer
	{
		#region Inner Types
		private class IntervalSequenceAccessor : ISequenceAccessor
		{
			private readonly int _first;
			private readonly int _last;

			public IntervalSequenceAccessor(int first, int last)
			{
				_first = first;
				_last = last;
			}

			#region ISequenceAccessor Members
			int ISequenceAccessor.Count
			{
				get { return Math.Abs(_last - _first) + 1; }
			}

			IFunction ISequenceAccessor.this[int index]
			{
				get
				{
					if (index < 0 || index >= ((ISequenceAccessor)this).Count)
					{
						throw new IndexOutOfRangeException();
					}

					return (_last > _first)
						? new IntegerFunction(_first + index)
						: new IntegerFunction(_first - index);
				}
			}
			#endregion

			#region IEnumerable<IFunction> Members
			IEnumerator<IFunction> IEnumerable<IFunction>.GetEnumerator()
			{
				if (_last >= _first)
				{
					for (int i = _first; i <= _last; ++i)
					{
						yield return new IntegerFunction(i);
					}
				}
				else
				{
					for (int i = _first; i >= _last; --i)
					{
						yield return new IntegerFunction(i);
					}
				}
			}
			#endregion

			#region IEnumerable Members
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<IFunction>)this).GetEnumerator();
			}
			#endregion
		}
		#endregion

		private static readonly string[] PARAMETER_NAMES = new[] { "first", "last" };

		/// <summary>Name of the function.</summary>
		public const string IntervalSequenceFunctionName = "IntervalSequence";

		#region IFunction Members
		string IFunction.FunctionName
		{
			get { return IntervalSequenceFunctionName; }
		}

		string[] IFunction.ParameterNames
		{
			get { return PARAMETER_NAMES; }
		}

		IFunctionParameterBinding[] IFunction.DefaultParameterValues
		{
			get { return FunctionHelper.EmptyBindings; }
		}

		IFunction IFunction.Simplify(SimplificationMode simplificationMode, params IFunction[] parameters)
		{
			return null;
		}

		IFunction IFunction.AcceptVisitor(IFunctionVisitor visitor)
		{
			return null;
		}
		#endregion

		#region ISequenceFunction Members
		ISequenceAccessor ISequenceFunction.GetAccessor(params IFunction[] parameters)
		{
			FunctionHelper.ValidateParameters(this, parameters);

			IntegerFunction integerFirst = parameters[0] as IntegerFunction;
			IntegerFunction integerLast = parameters[1] as IntegerFunction;

			if (integerFirst != null && integerLast != null)
			{
				return new IntervalSequenceAccessor(integerFirst.Value, integerLast.Value);
			}
			else
			{
				return null;
			}
		}
		#endregion

		#region IFunctionSerializer Members
		void IFunctionSerializer.Serialize(AppendOnlyStringBuilder builder, params IFunction[] parameters)
		{
			if (builder == null)
			{
				throw new ArgumentNullException("builder");
			}
			FunctionHelper.ValidateParameters(this, parameters);

			IFunction first = parameters[0];
			IFunction last = parameters[1];

			builder.Append("{");
			FunctionHelper.Serialize(first, builder);
			builder.Append(" .. ");
			FunctionHelper.Serialize(last, builder);
			builder.Append("}");
		}
		#endregion
	}
}